﻿namespace MvvmTools
{
    using System;
    using System.Diagnostics.Contracts;
    using JetBrains.Annotations;

    /// <summary>
    /// The notification about change of property.
    /// </summary>
    public struct PropertyChange : IEquatable<PropertyChange>
    {
        /// <summary>
        /// Default instance to notify about change of all properties.
        /// </summary>
        public static readonly PropertyChange All = new PropertyChange(0, string.Empty);

        private readonly int _changeId;

        /// <summary>
        /// Initializes a new instance of the <see cref="PropertyChange"/> structure.
        /// </summary>
        /// <param name="changeId">The change id.</param>
        /// <param name="propertyName">Name of the property.</param>
        public PropertyChange(int changeId, [NotNull] string propertyName)
            : this()
        {
            Contract.Requires<ArgumentNullException>(propertyName != null);

            _changeId = changeId;
            PropertyName = propertyName;
        }

        /// <summary>
        /// Gets the name of the property.
        /// </summary>
        /// <value>
        /// The name of the property.
        /// </value>
        public string PropertyName { [NotNull] get; private set; }

        /// <summary>
        /// Performs an implicit conversion from <see cref="PropertyChange"/> to <see cref="System.String"/>.
        /// </summary>
        /// <param name="change">The change.</param>
        /// <returns>
        /// The result of the conversion.
        /// </returns>
        public static implicit operator string(PropertyChange change)
        {
            return change.PropertyName;
        }

        /// <summary>
        /// Implements the operator ==.
        /// </summary>
        /// <param name="left">The left operand.</param>
        /// <param name="right">The right operand.</param>
        /// <returns>
        /// The result of the operator.
        /// </returns>
        public static bool operator ==(PropertyChange left, PropertyChange right)
        {
            return left.Equals(right);
        }

        /// <summary>
        /// Implements the operator !=.
        /// </summary>
        /// <param name="left">The left operand.</param>
        /// <param name="right">The right operand.</param>
        /// <returns>
        /// The result of the operator.
        /// </returns>
        public static bool operator !=(PropertyChange left, PropertyChange right)
        {
            return !left.Equals(right);
        }

        /// <summary>
        /// Indicates whether the current object is equal to another object of the same type.
        /// </summary>
        /// <returns>
        /// true if the current object is equal to the <paramref name="other"/> parameter; otherwise, false.
        /// </returns>
        /// <param name="other">An object to compare with this object.
        /// </param>
        public bool Equals(PropertyChange other)
        {
            return _changeId == other._changeId && string.Equals(PropertyName, other.PropertyName);
        }

        /// <summary>
        /// Indicates whether this instance and a specified object are equal.
        /// </summary>
        /// <returns>
        /// true if <paramref name="obj"/> and this instance are the same type and represent the same value; otherwise, false.
        /// </returns>
        /// <param name="obj">Another object to compare to. 
        /// </param><filterpriority>2</filterpriority>
        public override bool Equals(object obj)
        {
            if (ReferenceEquals(null, obj))
            {
                return false;
            }

            return obj is PropertyChange && Equals((PropertyChange)obj);
        }

        /// <summary>
        /// Returns the hash code for this instance.
        /// </summary>
        /// <returns>
        /// A 32-bit signed integer that is the hash code for this instance.
        /// </returns>
        /// <filterpriority>2</filterpriority>
        public override int GetHashCode()
        {
            unchecked
            {
                return (_changeId * 397) ^ PropertyName.GetHashCode();
            }
        }
    }
}